/*
 * Copyright (c) 1995 - 2008 Sun Microsystems, Inc.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Sun Microsystems nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package server;

import java.net.*;
import java.io.*;
import java.util.*;
import java.util.AbstractMap.SimpleEntry;
import java.util.logging.Level;
import java.util.logging.Logger;

public class KKMultiServerThread extends Thread implements BroadcastMsg {

    private Socket socket = null;
    //private PrintWriter out = null;
    //private BufferedReader in = null;
    private String username = null;
    private static int static_thread_id = 0;
    private int thread_id = 0;
    private int connect_id = 0;
    private boolean stop_parse = false;
    //private static HashSet users = new HashSet();
    PrintWriter input_log_out = null;// = new PrintWriter(new FileWriter("input_" + connect_id + ".txt"));
    PrintWriter output_log_out = null;// = new PrintWriter(new FileWriter("input_" + connect_id + ".txt"));

    void send_move_client(PrintWriter out, String reply) {
        //throw new UnsupportedOperationException("Not yet implemented");
         out.println("/move " + reply);
        output_log_out.println(reply);
        output_log_out.flush();
    }

    void send_change_client(PrintWriter out, String reply) {
        out.println("/change " + reply);
        output_log_out.println(reply);
        output_log_out.flush();
    }

    void send_kick_client(PrintWriter out, String reply) {
        out.println("/kick " + reply);
        output_log_out.println(reply);
        output_log_out.flush();
    }

    private void send_msg_client(PrintWriter out, String reply) {
        out.println("/msg " + reply);
        output_log_out.println(reply);
        output_log_out.flush();
    }

    void send_post_client(PrintWriter out, String reply) {
        out.println("/post " + reply);
        output_log_out.println(reply);
        output_log_out.flush();
    }

    public enum sendMsgType {

        LOGIN, LEAVE
    }
    private sendMsgType smg_type = sendMsgType.LOGIN;

    public static class ChatIO {

        public Socket socket = null;
        public PrintWriter out = null;
        public BufferedReader in = null;

        public ChatIO(Socket s) {
            socket = s;
            try {
                out = new PrintWriter(socket.getOutputStream(), true);
                in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            } catch (IOException e) {
                System.err.println("exception happen!!");
                e.printStackTrace();
            }
        }
    }
    private static HashMap<String, ChatIO> chat_map = new HashMap<String, ChatIO>();
    private ChatIO ctio = null;

    public static HashMap<String, ChatIO> getChatMap() {
        return chat_map;
    }
    private static HashMap<Integer, SimpleEntry<String,String> > msg_queue = new LinkedHashMap<Integer,  SimpleEntry<String,String> >();
    private static Map<String,String> wb_msg_queue =  new LinkedHashMap<String, String>();

    public static Map<String, String> getWbMsgQueue() {
        return wb_msg_queue;
    }

    public static HashMap<Integer,  SimpleEntry<String,String> > getMsgQueue() {
        return msg_queue;
    }
    private static int msg_id = 100;
    public static int wb_msg_id = 1;

    public void sendMsg(PrintWriter out, KKMultiServerThread server) {
        switch (smg_type) {
            case LOGIN:
                send_reply_client(out, username + " is connecting to the chat server");
                break;
            case LEAVE:
                send_reply_client(out, username + " is leaving the chat server");
                break;

            default:
                //System.out.println("Midweek days are so-so.");
                break;
        }

    }

    public void leave() {
        stop_parse = true;
        chat_map.remove(username);
        broadcast_leave();

    }

    public int add_msg_queue(String author,String msg) {
        int new_id = msg_id_gen();
        msg_queue.put(new_id, new SimpleEntry<String,String>(author,msg));
        return new_id;
    }

    public int msg_id_gen() {
        msg_id++;
        return msg_id;
    }

    public void rm_msg_queue(Integer index) throws NoSuchMsgItemException {
        if (msg_queue.containsKey(index)) {
            //PrintWriter out = ((KKMultiServerThread.ChatIO) KKMultiServerThread.getChatMap().get(someone)).out;
            msg_queue.remove(index);
        }// else throw exception
        else {
            throw new NoSuchMsgItemException(index.toString());
        }
    }

    // add /show
    public void send_reply_client(PrintWriter out, String reply) {
        out.println("/show " + reply);
        output_log_out.println(reply);
        output_log_out.flush();
    }

    public void send_obj_client(PrintWriter out, String reply) {
        out.println("/obj " + reply);
        output_log_out.println(reply);
        output_log_out.flush();
    }

    public String getUser() {
        return username;
    }

    public KKMultiServerThread(Socket socket, int id) {

        super("KKMultiServerThread");
        this.socket = socket;
        //ctio = new ChatIO(this.socket);

        thread_id = static_thread_id++;
        connect_id = id;
        try {
            input_log_out = new PrintWriter(new FileWriter("input_" + connect_id + ".txt"));
            output_log_out = new PrintWriter(new FileWriter("output_" + connect_id + ".txt"));
        } catch (IOException e) {
            System.err.println(e);
        }



    }

    public void show_all_bean()
    {
        Set set = this.wb_msg_queue.entrySet();
        Iterator It = set.iterator();

        int n = 0;
        while (It.hasNext()) {
            Map.Entry me = (Map.Entry) It.next();
            ctio.out.println("/obj" + " " + me.getKey()+" " +me.getValue());
        }
    }

    public void show_all_user() {
        Set set = chat_map.entrySet();

        Iterator It = set.iterator();

        int n = 0;
        while (It.hasNext()) {
            Map.Entry me = (Map.Entry) It.next();
            //System.out.println(me.getKey() + " : " + me.getValue() );
            System.out.println("" + ++n + ": " + me.getKey());
        }
    }

    public void prompt() {
        ctio.out.println(username + ">");
    }

    public void parse_command() {
        //prompt();
        String command = null;
        while (stop_parse == false) {
            try {

                command = ctio.in.readLine();  // tell student fuck
                input_log_out.println(command);
                input_log_out.flush();

                if (command == null) {
                    System.out.println("command: " + command + "fukcjjjjjjjjjjjj");
                    //break;
                }
                System.out.println("command: " + command);

                //ChatFormat c = new Yell(command);
                //ChatFormat c = new Yell(command);
                String[] names = command.split(" ");
                String cmd = names[0].substring(1);


                Class<?> classFor = Class.forName("server."+ cmd);
                java.lang.reflect.Constructor con = classFor.getConstructor(String.class);
                ChatFormat c = (ChatFormat) con.newInstance(command);
                //new tell

                c.execute(this);

            } catch (StringIndexOutOfBoundsException e) {
            } catch (SocketException eof) {
                System.out.println("Client terminated connection");
                break;
            } catch (ClassNotFoundException e) {
                //**** Your message command '......' is incorrect
                //ctio.out.println("");
                send_reply_client(ctio.out, "**** Your message command '" + command + "' is incorrect");
            } catch (IOException e) {
                e.printStackTrace();
                break;
            } catch (Exception e) {
                System.err.println(e);

            }

        }

    }

    public void welcome() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException ex) {
            Logger.getLogger(KKMultiServerThread.class.getName()).log(Level.SEVERE, null, ex);
        }
        ctio.out.println("/show *******************************************");
        
        ctio.out.println("/show ** " + username + " , welcome to the chat system.");
       
        ctio.out.println("/show *******************************************");


        show_all_bean();
    }

    public void login() {
        String name = null;
        while (true) {
            System.out.println("Login");
            ctio.out.println("/show Username:");

            try {
                name = ctio.in.readLine();
                System.out.println(thread_id + "(thread):" + name);
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (name.compareTo("") == 0) {
                ctio.out.println("/show No username is input.");
            } //else if(users.add(name) == false){
            else if (chat_map.containsKey(name) == true) {
                System.out.println("/sohw Please change a name.");
                ctio.out.println("/show Error: The user '" + name + "' is already online. Please change a name.");
            } else {
                ctio.out.println("/LoginOK " + name);    // protocol
                break;
            }

        }
        if (name == null) {
            System.err.println("[welcome]: name is null!!");
            System.exit(1);
        } else {
            username = name;
            System.out.println(username);
            /// 公子乖 -　ChatClient 通知 server 可以傳送歡迎訊息了!
            try {
                if (ctio.in.readLine().compareTo("/client_ready") == 0) {
                }
            } catch (IOException ex) {
                Logger.getLogger(KKMultiServerThread.class.getName()).log(Level.SEVERE, null, ex);
            }
            welcome();
            // �[�J hashmap
            chat_map.put(username, ctio);
            show_all_user();
        }
    }

    public void show_msg_queue(PrintWriter out) {
        SortedSet<Integer> sortedset = new TreeSet<Integer>(msg_queue.keySet());

        Iterator<Integer> it = sortedset.iterator();

        while (it.hasNext()) {
            Integer msg_id = (Integer) it.next();
            //Message 101<Student1>: "This is a test".    <-- The server sends "/msg 101 Student1 This is a test".
            send_msg_client(out, msg_id + " " + (String) msg_queue.get(msg_id).getKey() + " " + (String) msg_queue.get(msg_id).getValue());
        }

    }

    public void broadcast_login() {
        smg_type = sendMsgType.LOGIN;
        broadcast(this);
    }

    public void broadcast_leave() {
        smg_type = sendMsgType.LEAVE;
        broadcast(this);
    }

    public void broadcast(BroadcastMsg cft) {
        Set set = chat_map.entrySet();
        Iterator It = set.iterator();

        while (It.hasNext()) {
            Map.Entry me = (Map.Entry) It.next();
            PrintWriter out = ((KKMultiServerThread.ChatIO) me.getValue()).out;
            ((BroadcastMsg) cft).sendMsg(out, this);
        }
    }

    public void run() {

        try {
            ctio = new ChatIO(this.socket);
            String inputLine, outputLine;

            login();


            // sleep for client ChatClientThread boost
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                //System.out.println("Sleep100");
                System.err.println(e);
            }
            show_msg_queue(ctio.out);

            //* For each new created connection, broadcast to everyone
            //a message of "someone is connecting to the chat server".
            broadcast_login();

            parse_command();


            input_log_out.close();
            output_log_out.close();

            ctio.out.close();
            ctio.in.close();
            socket.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
